package com.dcy.quartz.service;

import com.dcy.common.base.service.DcyBaseService;
import com.dcy.common.constant.ScheduleConstants;
import com.dcy.common.exception.BusinessException;
import com.dcy.common.exception.TaskException;
import com.dcy.common.model.PageModel;
import com.dcy.common.model.PageResult;
import com.dcy.quartz.dao.JobDao;
import com.dcy.quartz.vo.in.JobChangeInVO;
import com.dcy.quartz.vo.in.JobCreateInVO;
import com.dcy.quartz.vo.in.JobSearchInVO;
import com.dcy.quartz.vo.in.JobUpdateInVO;
import com.dcy.quartz.vo.out.JobListOutVO;
import com.dcy.quartz.convert.JobConvert;
import com.dcy.quartz.enums.ScheduleApiErrorCode;
import com.dcy.quartz.model.Job;
import com.dcy.quartz.util.CronUtils;
import com.dcy.quartz.util.ScheduleUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * <p>
 * 定时任务调度表 服务实现类
 * </p>
 *
 * @author dcy
 * @since 2021-04-03
 */
@RequiredArgsConstructor
@Slf4j
@Service
public class JobService extends DcyBaseService {

    private final Scheduler scheduler;
    private final JobDao jobDao;
    private final JobConvert jobConvert = JobConvert.INSTANCE;

    /**
     * 获取表格数据
     *
     * @param jobSearchInVO
     * @param pageModel
     * @return
     */
    public PageResult<JobListOutVO> pageListByEntity(JobSearchInVO jobSearchInVO, PageModel pageModel) {
        return toPageResult(jobDao.pageListByEntity(jobConvert.toJob(jobSearchInVO), pageModel), page -> jobConvert.toOutputList(page.getRecords()));
    }

    /**
     * 运行一次任务
     *
     * @param jobId
     * @throws SchedulerException
     */
    public void run(String jobId) throws SchedulerException {
        Job tmpObj = jobDao.getById(jobId);
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, tmpObj);
        scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, tmpObj.getJobGroup()), dataMap);
    }

    /**
     * 删除job
     *
     * @param id
     * @throws SchedulerException
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteJobById(String id) throws SchedulerException {
        Job job = jobDao.getById(id);
        deleteJob(job);
    }

    /**
     * 批量删除
     *
     * @param ids
     * @throws SchedulerException
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteJobByIds(List<String> ids) throws SchedulerException {
        for (String jobId : ids) {
            Job job = jobDao.getById(jobId);
            deleteJob(job);
        }
    }

    private void deleteJob(Job job) throws SchedulerException {
        String jobId = job.getId();
        String jobGroup = job.getJobGroup();
        if (jobDao.removeById(jobId)) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }


    /**
     * 切换状态
     *
     * @param job
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean changeStatus(JobChangeInVO jobChangeInVO) throws SchedulerException {
        Job job = jobConvert.toJob(jobChangeInVO);
        if (ScheduleConstants.Status.NORMAL.getValue().equals(job.getJobStatus())) {
            resumeJob(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(job.getJobStatus())) {
            pauseJob(job);
        }
        return true;
    }

    /**
     * 恢复任务
     *
     * @param job
     * @return
     * @throws SchedulerException
     */
    private void resumeJob(Job job) throws SchedulerException {
        String jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setJobStatus(ScheduleConstants.Status.NORMAL.getValue());
        if (jobDao.updateById(job)) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }


    /**
     * 暂停任务
     *
     * @param job
     * @return
     * @throws SchedulerException
     */
    private void pauseJob(Job job) throws SchedulerException {
        String jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setJobStatus(ScheduleConstants.Status.PAUSE.getValue());
        if (jobDao.updateById(job)) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 添加job
     *
     * @param jobCreateInVO
     * @return
     * @throws SchedulerException
     */
    public Boolean saveJob(JobCreateInVO jobCreateInVO) throws SchedulerException {
        if (!CronUtils.isValid(jobCreateInVO.getCronExpression())) {
            throw new BusinessException(ScheduleApiErrorCode.CRON_EXPRESSION_ERROR);
        }
        Job job = jobConvert.toJob(jobCreateInVO);
        if (jobDao.save(job)) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return true;
    }

    /**
     * 修改job
     *
     * @param jobUpdateInVO
     * @return
     * @throws SchedulerException
     */
    public Boolean updateJob(JobUpdateInVO jobUpdateInVO) throws SchedulerException {
        if (!CronUtils.isValid(jobUpdateInVO.getCronExpression())) {
            throw new BusinessException(ScheduleApiErrorCode.CRON_EXPRESSION_ERROR);
        }
        Job job = jobConvert.toJob(jobUpdateInVO);
        if (jobDao.updateById(job)) {
            updateSchedulerJob(job, job.getJobGroup());
        }
        return true;
    }

    /**
     * 更新任务
     *
     * @param job      任务对象
     * @param jobGroup 任务组名
     */
    private void updateSchedulerJob(Job job, String jobGroup) throws SchedulerException, TaskException {
        String jobId = job.getId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
}
